package com.yc.nfc;

import android.media.SoundPool;
import android.util.Log;

import com.attendance.sz.utils.MyApplication;
import com.attendance.sz.utils.Session;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidParameterException;
import java.util.Arrays;

import static com.yc.nfc.Convert.ByteArrToHex;

/**
 * Created by Administrator on 2016-07-11.
 */
public class NfcClient {
    private static final String LOGTAG = "NfcClient";
    private SerialPort mSerialPort;
    private OutputStream mOutputStream;
    private InputStream mInputStream;
    private ReadThread mReadThread;
    private SendThread mSendThread;
    private String sPort = "/dev/s3c2410_serial0";
    private int iBaudRate = 9600;
    private boolean _isOpen = false;
    private byte[] _bLoopData = new byte[]{0x30};
    private int iDelay = 500;
    private SoundPool soundPool;
    byte mSector;
    byte[] mKeys;
    byte    Sector;
    //   Bundle bundle =new Bundle();
    public NfcClient(byte sector, byte[] keys) {
        this("/dev/ttyS5", 115200);
        this.mSector = sector;
        this.mKeys = keys;
    }

    public NfcClient(int com, byte sector, byte[] keys) {
        this("/dev/ttyS" + com, 115200);
        this.mSector = sector;
        this.mKeys = keys;

    }

    //----------------------------------------------------
    public NfcClient(String sPort, int iBaudRate) {
        this.sPort = sPort;
        this.iBaudRate = iBaudRate;
    }

    public NfcClient() {
        this("/dev/ttyS5", 115200);
    }

    public NfcClient(String sPort) {
        this(sPort, 9600);
    }

    public NfcClient(String sPort, String sBaudRate) {
        this(sPort, Integer.parseInt(sBaudRate));
    }

    //----------------------------------------------------
    public void open() throws SecurityException, IOException, InvalidParameterException {
        mSerialPort = new SerialPort(new File(sPort), iBaudRate, 0);
        mOutputStream = mSerialPort.getOutputStream();
        mInputStream = mSerialPort.getInputStream();
//        mReadThread = new ReadThread();
//        mReadThread.start();
//        mSendThread = new SendThread();
//        mSendThread.setSuspendFlag();
//        mSendThread.start();
        _isOpen = true;


    }

    //----------------------------------------------------
    public void close() {
//        if (mReadThread != null)
//            mReadThread.interrupt();
        if (mSerialPort != null) {
            mSerialPort.close();
            mSerialPort = null;
        }
        _isOpen = false;
    }

    //----------------------------------------------------
    public void send(byte[] bOutArray) {
        try {
            mOutputStream.write(bOutArray);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //----------------------------------------------------
    public void sendHex(String sHex) {
        byte[] bOutArray = Convert.HexToByteArr(sHex);
        send(bOutArray);
    }

    //----------------------------------------------------
    public void sendTxt(String sTxt) {
        byte[] bOutArray = sTxt.getBytes();
        send(bOutArray);
    }

    //----------------------------------------------------
    private class ReadThread extends Thread {
        //private static final String STORAGE_SERVICE22 = "122133";

        @Override
        public void run() {
            super.run();
            while (!isInterrupted()) {
                //Log.i(STORAGE_SERVICE22, "44444444444444444444");
                try {
                    if (mInputStream == null) return;

                    byte[] buffer = new byte[512];
                    int size = mInputStream.read(buffer);
                    if (size > 6) {
                        ComBean ComRecData = new ComBean(sPort, buffer, size);

                    }
                    try {
                        Thread.sleep(50);//延时50ms
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } catch (Throwable e) {
                    e.printStackTrace();
                    //Log.i(STORAGE_SERVICE22, "66666666666666666666666");
                    return;
                }
            }
        }
    }

    //----------------------------------------------------
    private class SendThread extends Thread {
        public boolean suspendFlag = true;// 控制线程的执行

        @Override
        public void run() {
            super.run();
            while (!isInterrupted()) {
                synchronized (this) {
                    while (suspendFlag) {
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                send(getbLoopData());
                try {
                    Thread.sleep(iDelay);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        //线程暂停
        public void setSuspendFlag() {
            this.suspendFlag = true;
        }

        //唤醒线程
        public synchronized void setResume() {
            this.suspendFlag = false;
            notify();
        }
    }

    //----------------------------------------------------
    public int getBaudRate() {
        return iBaudRate;
    }

    public boolean setBaudRate(int iBaud) {
        if (_isOpen) {
            return false;
        } else {
            iBaudRate = iBaud;
            return true;
        }
    }

    public boolean setBaudRate(String sBaud) {
        int iBaud = Integer.parseInt(sBaud);
        return setBaudRate(iBaud);
    }

    //----------------------------------------------------
    public String getPort() {
        return sPort;
    }

    public boolean setPort(String sPort) {
        if (_isOpen) {
            return false;
        } else {
            this.sPort = sPort;
            return true;
        }
    }

    //----------------------------------------------------
    public boolean isOpen() {
        return _isOpen;
    }

    //----------------------------------------------------
    public byte[] getbLoopData() {
        return _bLoopData;
    }

    //----------------------------------------------------
    public void setbLoopData(byte[] bLoopData) {
        this._bLoopData = bLoopData;
    }

    //----------------------------------------------------
    public void setTxtLoopData(String sTxt) {
        this._bLoopData = sTxt.getBytes();
    }

    //----------------------------------------------------
    public void setHexLoopData(String sHex) {
        this._bLoopData = Convert.HexToByteArr(sHex);
    }

    //----------------------------------------------------
    public int getiDelay() {
        return iDelay;
    }

    //----------------------------------------------------
    public void setiDelay(int iDelay) {
        this.iDelay = iDelay;
    }

    //----------------------------------------------------
    public void startSend() {
        if (mSendThread != null) {
            mSendThread.setResume();
        }
    }

    //----------------------------------------------------
    public void stopSend() {
        if (mSendThread != null) {
            mSendThread.setSuspendFlag();
        }
    }
    //----------------------------------------------------

    public byte[] communication(String hex) {
        return communication(Convert.HexToByteArr(hex));
    }

    public byte[] communication(byte[] send) {
        send(send);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        byte[] recv = null;
        int timeOut = 500;
        while (true) {
            if (timeOut <= 0)
                break;
            timeOut--;
            try {
                if (mInputStream.available() > 0) {
                    recv = receive();
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
//        System.out.println("send:"+Convert.ByteArrToHex(send));
//        if(recv!=null)
//            System.out.println("recv:"+Convert.ByteArrToHex(recv));

        return recv;
    }


    private byte[] receive() {
        byte[] buffer = new byte[512];
        byte[] bRec = null;
        try {
            int size = mInputStream.read(buffer);
            bRec = new byte[size];
            for (int i = 0; i < size; i++) {
                bRec[i] = buffer[i];
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bRec;
    }

    byte[] name;
    private static final String ERROR_WAKEUP = "唤醒失败";
    private static final String ERROR_SELECT = "寻卡失败";
    private static final String ERROR_AUTH = "认证失败";
    private static final String ERROR_READ = "读卡失败";
    private static final String ERROR_WRITE = "写卡失败";
    boolean isWakeup = false;

    public CardInfo readName() {
        if (!isWakeup) {
            isWakeup = nfcWakeup();
            if (!isWakeup) {
                MyApplication.getInstance().setgstrDestIP("3");
                Log.d(LOGTAG, ERROR_WAKEUP);
                return null;
            }
        }

        byte[] currentCardNum = nfcSelectCard();
        if (currentCardNum == null) {
            Log.d(LOGTAG, ERROR_SELECT);
            MyApplication.getInstance().setgstrDestIP("3");
            return null;
        }

        byte[] retPwd = new byte[6];
        CardUtils.getAnthCode(currentCardNum, mKeys, retPwd);
        //认证卡片
        if (!nfcAnth(mSector, retPwd, currentCardNum, (byte) 0x60)) {
            Log.d(LOGTAG, ERROR_AUTH);
            if (new String(currentCardNum).equals(Session.getCardName())) {
                MyApplication.getInstance().setgstrDestIP("4");
            } else {
            }
            Session.setCardName(new String(currentCardNum));
            return null;
        }

        byte[][] buffer = new byte[4][16];

        for (int i = 0; i < 4; i++) {
            if (!nfcReadBlock((byte) (mSector * 4 + i), buffer[i])) {
                Log.d(LOGTAG, ERROR_READ);
                MyApplication.getInstance().setgstrDestIP("3");
                return null;
            }
        }
        CardInfo info = null;
        byte[] cardId = new byte[4];
        cardId[0] = currentCardNum[3];
        cardId[1] = currentCardNum[2];
        cardId[2] = currentCardNum[1];
        cardId[3] = currentCardNum[0];
//        info = CardUtils.convertBufferToClass(buffer);
        String hexx = Convert.ByteArrToHex(cardId, null);
        BigInteger idd = new BigInteger(hexx, 16);
        if (String.valueOf(idd.longValue()).equals(MyApplication.getApp().getCard())) {
            MyApplication.getInstance().setgstrDestIP("6");
            return null;
        }
        if (info != null) {
            info.setCardID(cardId);
            MyApplication.getInstance().setgstrDestIP("3");

        } else {
            byte[] total = new byte[64];
            int c = 0;
            for (int j = 0; j < 4; j++) {
                for (int i = j * 16; i < (j + 1) * 16; i++) {
                    if (c > 15) {
                        c = 0;
                    }
                    total[i] = buffer[j][c++];
                }
            }
            byte[] number = new byte[total[0]];
            name = new byte[total[total[0] + 1]];
            for (int i = 1; i < total[0] + 1; i++) {
                number[i - 1] = total[i];
            }
            for (int i = total[0] + 2; i < total[0] + 2 + total[total[0] + 1]; i++) {
                name[i - total[0] - 2] = total[i];
            }

            try {
//                CardSerial
                String hex = ByteArrToHex(cardId, null);
                BigInteger id = new BigInteger(hex, 16);
                MyApplication.getInstance().setName(new String(name, "GB2312"));
                MyApplication.getInstance().setNumber(new String(number, "GB2312"));
                MyApplication.getInstance().setCard(String.valueOf(id.longValue()));
                MyApplication.getInstance().setCardSQ(ByteArrToHex(currentCardNum));
                MyApplication.getInstance().setgstrDestIP("0");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        return info;

    }
    CardInfo info = new CardInfo();
    public CardInfo read() {
        Sector =(byte)(Integer.parseInt(Session.getCarSection())-1);
        if (!this.isWakeup) {
            this.isWakeup = this.nfcWakeup();
            if (!this.isWakeup) {
                Log.d("NfcClient", "唤醒失败");

                return null;
            }
        }

        byte[] currentCardNum = this.nfcSelectCard();
        if (currentCardNum == null) {
            Log.d("NfcClient", "寻卡失败");

            return null;
        } else {
            byte[] retPwd = new byte[6];
            CardUtils.getAnthCode(currentCardNum, mKeys, retPwd);
            if (!this.nfcAnth(this.Sector, retPwd, currentCardNum, (byte) 96)) {
                Log.d("NfcClient", "认证失败");
                if (new String(currentCardNum).equals(Session.getCardName())) {
                    Log.d("TTT","111");
                    MyApplication.getInstance().getInstance().setgstrDestIP("111");
                }
                Session.setCardName(new String(currentCardNum));
//                MyApplication.getInstance().setgstrDestIP("111");
                return null;
            } else {
                byte[][] buffer = new byte[4][16];

                for (int cardId = 0; cardId < 4; ++cardId) {
                    if (!this.nfcReadBlock((byte) (this.Sector * 4 + cardId), buffer[cardId])) {
                        Log.d("NfcClient", "读卡失败");
                        return null;
                    }
                }

                byte[] var6 = new byte[]{currentCardNum[3], currentCardNum[2], currentCardNum[1], currentCardNum[0]};
                 info = CardUtils.convertBufferToClass(buffer);
                if (info != null) {
                    info.setCardID(var6);
                 MyApplication.getInstance().setCardSerial(info.getCardSerial());
                    Log.d("TAG111","卡号：nfc"+info.getCardID());
                    Log.d("TAG111","流水号：nfc"+info.getCardSerial());
                }
                readName();

                return info;
            }
        }
    }
    public boolean write(CardInfo info) {
        if (!isWakeup) {
            isWakeup = nfcWakeup();
            if (!isWakeup) {
                Log.d(LOGTAG, ERROR_WAKEUP);
                return false;
            }
        }

        byte[] currentCardNum = nfcSelectCard();
        if (currentCardNum == null) {
            Log.d(LOGTAG, ERROR_SELECT);
            return false;
        }

        byte[] retPwd = new byte[6];

        CardUtils.getAnthCode(currentCardNum, mKeys, retPwd);

        //认证卡片
        if (!nfcAnth(mSector, retPwd, currentCardNum, (byte) 0x60)) {
            Log.d(LOGTAG, ERROR_AUTH);
            return false;
        }
        byte[][] buffer = new byte[4][16];
        CardUtils.convertClassToBuffer(buffer, info);
        for (int i = 1; i < 3; i++) {
            if (!nfcWriteBlock((byte) (mSector * 4 + i), buffer[i])) {
                Log.d(LOGTAG, ERROR_WRITE);
                return false;
            }
        }

        return true;
    }

    private boolean nfcWakeup() {
        byte[] bRecvData = communication("55550000000000000000000000000000ff03fdD414011700".toString());
        if (bRecvData == null) {
            return false;
        }
        if (bRecvData.length <= 6) {
            Log.d(LOGTAG, "无效数据");
            return false;
        }
        byte[] RecvData = Arrays.copyOfRange(bRecvData, 6, bRecvData.length);
        if ((RecvData[0] == (byte) 0x00) && (RecvData[1] == (byte) 0x00) && (RecvData[2] == (byte) 0xff)) {
            if ((RecvData[3] == (byte) 0x02) && (RecvData[4] == (byte) 0xfe) && (RecvData[5] == (byte) 0xD5) && (RecvData[6] == (byte) 0x15) && (RecvData[7] == (byte) 0x16)) {
                return true;
            }

        }
        return false;

    }

    /*
     * 寻找卡片
     * return :1-成功  其他-失败
     * */
    private byte[] nfcSelectCard() {

        byte[] bRecvData = communication("0000ff04fcd44a0100e100".toString());
        if (bRecvData.length <= 6) {
            Log.d(LOGTAG, "无效数据");
            return null;
        }
        byte[] RecvData = Arrays.copyOfRange(bRecvData, 6, bRecvData.length);
        if ((RecvData[0] == (byte) 0x00) && (RecvData[1] == (byte) 0x00) && (RecvData[2] == (byte) 0xff)) {
            if ((RecvData[5] == (byte) 0xD5) && (RecvData[6] == (byte) 0x4B)) {

                if ((RecvData[9] == (byte) 0x00) && ((RecvData[10] == (byte) 0x02) || (RecvData[10] == (byte) 0x04))) {
                    byte[] UUID = new byte[4]; //物理卡号
                    for (int i = 0; i < 4; i++)
                        UUID[i] = RecvData[i + 13];
                    Log.d(LOGTAG, ByteArrToHex(UUID));

                    return UUID;
                } else if ((RecvData[9] == (byte) 0x00) && (RecvData[10] == (byte) 0x44)) {
                    byte[] UUID = new byte[7];   //物理卡号
                    for (int i = 0; i < 7; i++)
                        UUID[i] = RecvData[i + 13];
                    Log.d(LOGTAG, ByteArrToHex(UUID));
                    return UUID;
                } else {
                    Log.i(LOGTAG, "select failed");
                    return null;
                }


            }

        }
        return null;
    }

    /*
     * 卡片块写，M1卡规范，每一块只有16个字节
     * Addr-要写的卡片地址（4*扇区号，每个扇区有四个块，最大为15*4+3）,wBuffer-为一块的数据，写之前先拿到加密数据
     *
     * */
    private boolean nfcWriteBlock(byte Addr, byte[] wBuffer) {

        byte[] SendBuf = new byte[28];
        Arrays.fill(SendBuf, (byte) 0);
        if (Addr > 128) return false;

        SendBuf[0] = (byte) 0x00;
        SendBuf[1] = (byte) 0x00;
        SendBuf[2] = (byte) 0xff;
        SendBuf[3] = (byte) 0x15;
        SendBuf[4] = (byte) 0xEB;
        SendBuf[5] = (byte) 0xd4;
        SendBuf[6] = (byte) 0x40;
        SendBuf[7] = (byte) 0x01;
        SendBuf[8] = (byte) 0xa0;
        SendBuf[9] = Addr;
        for (int i = 0; i < 16; i++)
            SendBuf[10 + i] = wBuffer[i];
        byte checkSum = 0;
        for (int i = 0; i < 21; i++) {
            checkSum += SendBuf[i + 5];
        }
        SendBuf[26] = (byte) (~checkSum + 1);
        byte[] bRecvData = communication(SendBuf);

        if (bRecvData.length <= 6) {
            Log.d(LOGTAG, "无效数据");
            return false;
        }
        byte[] RecvData = Arrays.copyOfRange(bRecvData, 6, bRecvData.length);

        if ((RecvData[0] == (byte) 0x00) && (RecvData[1] == (byte) 0x00) && (RecvData[2] == (byte) 0xff)) {
            if ((RecvData[5] == (byte) 0xD5) && (RecvData[6] == (byte) 0x41) && (RecvData[7] == (byte) 0x00)) {
                return true;
            }
        }
        return false;
    }

    /*
     * 卡片块读，M1卡规范，每一块只有16个字节
     * Addr-要写的卡片地址（4*扇区号，每个扇区有四个块，最大为15*4+3）,rBuffer-为一块的数据，写之前先拿到加密数据
     *
     * */
    private boolean nfcReadBlock(byte Addr, byte[] rBuffer) {

        byte[] SendBuf = new byte[12];
        Arrays.fill(SendBuf, (byte) 0);
        if (Addr > 128) return false;

        SendBuf[0] = (byte) 0x00;
        SendBuf[1] = (byte) 0x00;
        SendBuf[2] = (byte) 0xff;
        SendBuf[3] = (byte) 0x05;
        SendBuf[4] = (byte) 0xfB;
        SendBuf[5] = (byte) 0xd4;
        SendBuf[6] = (byte) 0x40;
        SendBuf[7] = (byte) 0x01;
        SendBuf[8] = (byte) 0x30;
        SendBuf[9] = Addr;


        int CheckSum;
        int i;
        for (i = 0, CheckSum = 0; i < 5; i++) {
            CheckSum += SendBuf[i + 5];
        }
        SendBuf[10] = (byte) (~CheckSum + 1);
        byte[] bRecvData = communication(SendBuf);

        if (bRecvData.length <= 6) {
            Log.d(LOGTAG, "无效数据");
            return false;
        }
        byte[] RecvData = Arrays.copyOfRange(bRecvData, 6, bRecvData.length);

        if ((RecvData[0] == (byte) 0x00) && (RecvData[1] == (byte) 0x00) && (RecvData[2] == (byte) 0xff)) {
            if ((RecvData[5] == (byte) 0xD5) && (RecvData[6] == (byte) 0x41) && (RecvData[7] == (byte) 0x00)) {


                //取得得到块数据
                //byte[] CardBlock = new byte[16];
                for (i = 0; i < 16; i++)
                    rBuffer[i] = RecvData[i + 8];
                return true;
            }
        }
        return false;

    }

    /*
     * 认证卡片
     * Sector-扇区号（共16个扇区，0-15）;Keys-认证密钥;AnthType-认证类型（卡片有AB两个密码（60 A,61 B））
     * */
    private boolean nfcAnth(byte Sector, byte[] Keys, byte[] uuid, byte AnthType) {
        byte[] SendBuf = new byte[22];
        Arrays.fill(SendBuf, (byte) 0);

        if (Sector > 15) return false;

        SendBuf[0] = (byte) 0x00;
        SendBuf[1] = (byte) 0x00;
        SendBuf[2] = (byte) 0xff;
        SendBuf[3] = (byte) 0x0f;
        SendBuf[4] = (byte) 0xf1;
        SendBuf[5] = (byte) 0xd4;
        SendBuf[6] = (byte) 0x40;
        SendBuf[7] = (byte) 0x01;
        SendBuf[8] = (byte) AnthType;
        SendBuf[9] = (byte) (Sector * 4 + 3);

        for (int i = 0; i < 6; i++)
            SendBuf[10 + i] = Keys[i];
        for (int i = 0; i < 4; i++)
            SendBuf[16 + i] = uuid[i];


        int CheckSum;
        int i;
        for (i = 0, CheckSum = 0; i < 15; i++) {
            CheckSum += SendBuf[i + 5];
        }
        SendBuf[20] = (byte) (~CheckSum + 1);

        byte[] bRecvData = communication(SendBuf);
        if (bRecvData.length == 0) {
            Log.d(LOGTAG, "无效数据");
            return false;
        }
        if (bRecvData.length <= 6) {
            Log.d(LOGTAG, "无效数据");
            return false;
        }
        byte[] RecvData = Arrays.copyOfRange(bRecvData, 6, bRecvData.length);

        if (RecvData.length>3 && (RecvData[0] == (byte) 0x00) && (RecvData[1] == (byte) 0x00) && (RecvData[2] == (byte) 0xff)) {
            if ((RecvData[5] == (byte) 0xD5) && (RecvData[6] == (byte) 0x41) && (RecvData[7] == (byte) 0x00)) {
                return true;
            }
        }
        return false;

    }

}
